<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      #canvas {
        background-color: black;
        height: 800px;
        width: 800px;
      }
    </style>
  </head>
  <body>
    <div id="canvas"></div>
    <script type="importmap">
      {
        "imports": {
          "three": "../node_modules/three/build/three.module.js",
          "dat.gui": "../node_modules/dat.gui/build/dat.gui.module.js"
        }
      }
    </script>
    <script type="module">
      import * as THREE from 'three';
      import ThreeBase from './ThreeBase.js';
      import { initGeoFun, latlng2px } from './geoUtil.js';
      import { drawCircle } from './heatmap.js';
      initGeoFun(1000);
      class MyHeatmap extends ThreeBase {
        constructor() {
          super();

          this.initCameraPos = [-25, 288, 342];
        }

        createHeatmap() {
          return new Promise((resolve) => {
            fetch('./assets/traffic.json')
              .then((res) => res.json())
              .then((res) => {
                let info = {
                  max: Number.MIN_SAFE_INTEGER,
                  min: Number.MAX_SAFE_INTEGER,
                  maxlng: Number.MIN_SAFE_INTEGER,
                  minlng: Number.MAX_SAFE_INTEGER,
                  maxlat: Number.MIN_SAFE_INTEGER,
                  minlat: Number.MAX_SAFE_INTEGER,
                  data: []
                };
                res.features.forEach((item) => {
                  let pos = latlng2px(item.geometry.coordinates);
                  let newitem = {
                    lng: pos[0],
                    lat: pos[1],
                    value: item.properties.avg
                  };
                  info.max = Math.max(newitem.value, info.max);
                  info.maxlng = Math.max(newitem.lng, info.maxlng);
                  info.maxlat = Math.max(newitem.lat, info.maxlat);

                  info.min = Math.min(newitem.value, info.min);
                  info.minlng = Math.min(newitem.lng, info.minlng);
                  info.minlat = Math.min(newitem.lat, info.minlat);
                  info.data.push(newitem);
                });
                info.size = info.max - info.min;
                info.sizelng = info.maxlng - info.minlng;
                info.sizelat = info.maxlat - info.minlat;
                const radius = 40;
                const option = {
                  width: info.sizelng + radius * 2,
                  height: info.sizelng + radius * 2,
                  radius,
                  ...info
                };
                const canvas = document.createElement('canvas');
                canvas.width = option.width;
                canvas.height = option.height;
                const ctx = canvas.getContext('2d');
                option.data.forEach((item) => {
                  drawCircle(ctx, option, item);
                });
                resolve({ option, canvas });
              });
          });
        }

        async createChart(that) {
          const { canvas: heatmapCanvas, option } = await this.createHeatmap();

          const map = new THREE.CanvasTexture(heatmapCanvas);
          map.wrapS = THREE.RepeatWrapping;
          map.wrapT = THREE.RepeatWrapping;
          const geometry = new THREE.PlaneGeometry(
            option.width * 0.5,
            option.height * 0.5,
            500,
            500
          );

          const material = new THREE.ShaderMaterial({
            transparent: true,
            side: THREE.DoubleSide,
            uniforms: {
              //热力贴图
              map: { value: map },
              //山丘高度
              uHeight: { value: 50 },
              //热力信息：最小值，最大值，值范围，等高线高度间隔
              uInfo: { value: new THREE.Vector4(option.min, option.max, option.size, 10) },
              //等高线宽度
              uMinLne: { value: 0.3 },
              //等高线颜色
              uLineColor: { value: new THREE.Color('#ffffff') }
            },
            vertexShader: /* glsl */ `
//热力贴图
uniform sampler2D map;
//山丘高度
uniform float uHeight;
varying vec4 vColor;
void main(void) {
//热力贴图颜色               
    vColor = texture2D(map, uv);
//热力高度
    float h = vColor.a * uHeight;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x, position.y, h, 1.0);
}`,
            fragmentShader: /* glsl */ `         
                //等高线宽度
uniform float uMinLne;
//热力值信息
uniform vec4 uInfo;
//等高线颜色
uniform vec3 uLineColor;
varying vec4 vColor;
void main(void) { 
//还原热力值
    float v = vColor.a * uInfo.z + uInfo.x; 
//利用等高线高度间隔取模，在uMinLne范围内绘制等高线
    if(mod(v, uInfo.w) <= uMinLne) {
    //等高线颜色
        gl_FragColor.rgb = uLineColor;
        //透明度默认是1
        gl_FragColor.a = 1.;
    }
}`
          });
          const plane = new THREE.Mesh(geometry, material);
          plane.rotateX(-Math.PI * 0.5);
          this.scene.add(plane);
        }
      }

      var heatmap = new MyHeatmap();
      window.heatmap = heatmap;
      heatmap.initThree(document.getElementById('canvas'));
      heatmap.createChart();
    </script>
  </body>
</html>
